package gateway.filters;

import framework.config.SecurityConfig;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.http.HttpCookie;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.util.StringUtils;
import org.springframework.web.server.ServerWebExchange;
import security.filters.AuthenticationHandler;
import security.utils.SecurityCookieUtil;
import security.utils.TokenChannel;
import security.utils.TokenDetail;

import java.util.Objects;

public class AuthenticationWebFluxHandler implements AuthenticationHandler {

    private final ServerWebExchange exchange;
    private final ServerHttpRequest request;
    private final ServerHttpResponse response;
    private final GatewayFilterChain filterChain;
    private final SecurityConfig securityConfig;

    public AuthenticationWebFluxHandler(ServerWebExchange exchange, GatewayFilterChain filterChain, SecurityConfig securityConfig) {
        this.exchange = exchange;
        this.request = exchange.getRequest();
        this.response = exchange.getResponse();
        this.filterChain = filterChain;
        this.securityConfig = securityConfig;
    }

    /**
     * get token
     *
     * @return
     */
    @Override
    public TokenDetail getRequestToken() {
        String requestToken = request.getHeaders().getFirst("Authorization");
        boolean hasToken = StringUtils.hasLength(requestToken);
        if (hasToken && Objects.equals(true, securityConfig.getEnableBearerToken()) && requestToken.startsWith("Bearer ")) {
            return new TokenDetail(requestToken.substring(7), requestToken, TokenChannel.Bearer);
        }

        if (hasToken && Objects.equals(true, securityConfig.getEnableBasicAuth()) && requestToken.startsWith("Basic ")) {
            return new TokenDetail(requestToken.substring(6), requestToken, TokenChannel.Basic);
        }

//        if (hasToken && Objects.equals(true, securityConfig.getEnableBasicOauth()) && requestToken.startsWith("OAuth ")) {
//            return new TokenDetail(requestToken.substring(6), TokenChannel.OAuth);
//        }

        requestToken = request.getQueryParams().getFirst(securityConfig.getTokenParamName());
        if (StringUtils.hasText(requestToken)) {
            return new TokenDetail(requestToken, TokenChannel.Bearer.name() + " " + requestToken, TokenChannel.Query);
        }

        HttpCookie httpCookie = request.getCookies().getFirst(securityConfig.getTokenParamName());
        if (httpCookie != null && StringUtils.hasText(httpCookie.getValue())) {
            return new TokenDetail(httpCookie.getValue(), TokenChannel.Bearer.name() + " " + httpCookie.getValue(), TokenChannel.Cookie);
        }

        return null;
    }

    /**
     * 获取请求路径
     *
     * @return
     */
    @Override
    public String getRequestPath() {
        return request.getPath().value();
    }

    /**
     * 获取请求方法
     *
     * @return
     */
    @Override
    public String getRequestMethod() {
        HttpMethod method = request.getMethod();
        return method.name();
    }

    @Override
    public void tokenExpiredReminder(int seconds) {
        String tokenExpirationReminderHeader = securityConfig.getTokenExpirationReminderHeader();
        if (StringUtils.hasText(tokenExpirationReminderHeader)) {
            HttpHeaders headers = response.getHeaders();
            headers.add(tokenExpirationReminderHeader, "" + seconds);
        }
    }

    @Override
    public void tokenBalanceOutput(int seconds) {
        String tokenBalanceOutputHeader = securityConfig.getTokenBalanceOutputHeader();
        if (StringUtils.hasText(tokenBalanceOutputHeader)) {
            HttpHeaders headers = response.getHeaders();
            headers.add(tokenBalanceOutputHeader, "" + seconds);
        }
    }

    @Override
    public void saveTokenToCookie(String token) {
        String cookieName = securityConfig.getTokenParamName();
        if (!StringUtils.hasText(cookieName)) return;

        // 网关以配置路径为存储路径
        String cookiePath = securityConfig.getCookiePath();
        if (!StringUtils.hasText(cookiePath)) {
            // 网关必需配置该路径
            throw new RuntimeException("You need to configure security cookie path");
        }

        // 网关以配置时间为存储时间
        Integer expires = securityConfig.getCookieMaxAge();

        // fix HttpCookie not support SameSite
        // use custom build
        StringBuffer buffer = SecurityCookieUtil.buildCookie(securityConfig, cookieName, token, cookiePath, expires);
        response.getHeaders().add("Set-Cookie", buffer.toString());
    }
}
